From 2526ff6cc98fc971041442e63bfe43bdc8bed9d2 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Thu, 28 Oct 2010 12:19:43 +0100 Subject: [PATCH] libxl: Fix migration of HVM guests In the default "QemuDeviceModelRecord"-style HVM tail of the migration protocol the size of the qemu save record is unknown at the receiver and therefore it must read until EOF. This is not compatible with the xl migration protocol which contains a post-migration handshake and therefore cannot close the socket on the sending end. What is required is an explicit length field for the save record, which the "RemusDeviceModelState"-style HVM tail includes. Rather than overload the "RemusDeviceModelState" name for a non-Remus use case (on off chance that they need to diverge for some reason in the future) introduce a third style called "DeviceModelRecord0002" which is identical to current "RemusDeviceModelState"-style. Hopefully the inclusion of a number here will allow easier extension in the future without needing to come up with increasingly less helpful names! Also propagate errors from xc_domain_save and libxl__domain_suspend_common to callers. Signed-off-by: Ian Campbell Signed-off-by: Ian Jackson --- tools/libxc/xc_domain_restore.c | 13 +++++++++---- tools/libxc/xg_save_restore.h | 3 ++- tools/libxl/libxl.c | 4 ++-- tools/libxl/libxl_dom.c | 24 ++++++++++++++++++++++-- tools/libxl/libxl_internal.h | 2 +- 5 files changed, 36 insertions(+), 10 deletions(-) diff --git a/tools/libxc/xc_domain_restore.c b/tools/libxc/xc_domain_restore.c index c9f3916b34..7c64a4929b 100644 --- a/tools/libxc/xc_domain_restore.c +++ b/tools/libxc/xc_domain_restore.c @@ -494,13 +494,18 @@ static int buffer_tail_hvm(xc_interface *xch, struct restore_ctx *ctx, return -1; } - /* The normal live-migration QEMU record has no length information. + /* The legacy live-migration QEMU record has no length information. * Short of reimplementing the QEMU parser, we're forced to just read - * until EOF. Remus gets around this by sending a different signature - * which includes a length prefix */ + * until EOF. + * + * Gets around this by sending a different signatures for the new + * live-migration QEMU record and Remus which includes a length + * prefix + */ if ( !memcmp(qemusig, "QemuDeviceModelRecord", sizeof(qemusig)) ) return compat_buffer_qemu(xch, ctx, fd, buf); - else if ( !memcmp(qemusig, "RemusDeviceModelState", sizeof(qemusig)) ) + else if ( !memcmp(qemusig, "DeviceModelRecord0002", sizeof(qemusig)) || + !memcmp(qemusig, "RemusDeviceModelState", sizeof(qemusig)) ) return buffer_qemu(xch, ctx, fd, buf); qemusig[20] = '\0'; diff --git a/tools/libxc/xg_save_restore.h b/tools/libxc/xg_save_restore.h index 2c82ce7404..b13bdef649 100644 --- a/tools/libxc/xg_save_restore.h +++ b/tools/libxc/xg_save_restore.h @@ -104,8 +104,9 @@ * Qemu context: * char[21] : Signature: * "QemuDeviceModelRecord" : Read Qemu save data until EOF - * "RemusDeviceModelState" : uint32_t length field followed by that many + * "DeviceModelRecord0002" : uint32_t length field followed by that many * bytes of Qemu save data + * "RemusDeviceModelState" : Currently the same as "DeviceModelRecord0002". * * PV TAIL: * diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c index f40d41b7fc..79d9f0d319 100644 --- a/tools/libxl/libxl.c +++ b/tools/libxl/libxl.c @@ -686,8 +686,8 @@ int libxl_domain_suspend(libxl_ctx *ctx, libxl_domain_suspend_info *info, int debug = info != NULL && info->flags & XL_SUSPEND_DEBUG; int rc = 0; - libxl__domain_suspend_common(ctx, domid, fd, hvm, live, debug); - if (hvm) + rc = libxl__domain_suspend_common(ctx, domid, fd, hvm, live, debug); + if (!rc && hvm) rc = libxl__domain_save_device_model(ctx, domid, fd); return rc; } diff --git a/tools/libxl/libxl_dom.c b/tools/libxl/libxl_dom.c index 5352f65ef6..bbba8057e8 100644 --- a/tools/libxl/libxl_dom.c +++ b/tools/libxl/libxl_dom.c @@ -442,14 +442,17 @@ int libxl__domain_suspend_common(libxl_ctx *ctx, uint32_t domid, int fd, callbacks.switch_qemu_logdirty = libxl__domain_suspend_common_switch_qemu_logdirty; callbacks.data = &si; - xc_domain_save(ctx->xch, fd, domid, 0, 0, flags, &callbacks, hvm); + rc = xc_domain_save(ctx->xch, fd, domid, 0, 0, flags, &callbacks, hvm); + if ( rc ) { + LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "saving domain"); + rc = ERROR_FAIL; + } if (si.suspend_eventchn > 0) xc_suspend_evtchn_release(ctx->xch, si.xce, domid, si.suspend_eventchn); if (si.xce > 0) xc_evtchn_close(si.xce); - rc = 0; out: libxl__free_all(&gc); return rc; @@ -461,15 +464,32 @@ int libxl__domain_save_device_model(libxl_ctx *ctx, uint32_t domid, int fd) int fd2, c; char buf[1024]; char *filename = libxl__sprintf(&gc, "/var/lib/xen/qemu-save.%d", domid); + struct stat st; + uint32_t qemu_state_len; LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Saving device model state to %s", filename); libxl__xs_write(&gc, XBT_NULL, libxl__sprintf(&gc, "/local/domain/0/device-model/%d/command", domid), "save"); libxl__wait_for_device_model(ctx, domid, "paused", NULL, NULL); + if (stat(filename, &st) < 0) + { + LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Unable to stat qemu save file\n"); + return ERROR_FAIL; + } + + qemu_state_len = st.st_size; + LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Qemu state is %d bytes\n", qemu_state_len); + c = libxl_write_exactly(ctx, fd, QEMU_SIGNATURE, strlen(QEMU_SIGNATURE), "saved-state file", "qemu signature"); if (c) return c; + + c = libxl_write_exactly(ctx, fd, &qemu_state_len, sizeof(qemu_state_len), + "saved-state file", "saved-state length"); + if (c) + return c; + fd2 = open(filename, O_RDONLY); while ((c = read(fd2, buf, sizeof(buf))) != 0) { if (c < 0) { diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h index e89ac2fe9f..e3fc22dd9f 100644 --- a/tools/libxl/libxl_internal.h +++ b/tools/libxl/libxl_internal.h @@ -44,7 +44,7 @@ #define LIBXL_PV_EXTRA_MEMORY 1024 #define LIBXL_HVM_EXTRA_MEMORY 2048 #define LIBXL_MIN_DOM0_MEM (128*1024) -#define QEMU_SIGNATURE "QemuDeviceModelRecord" +#define QEMU_SIGNATURE "DeviceModelRecord0002" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) -- 2.30.2